package batchprocessor;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class BatchParser {

    // Private Variables
    int num_commands;
    String wkdir;
    List<List<String>> commands;
    List<List<String>> file_list;
    List<List<String>> pipes;

    public BatchParser(File file) throws ProcessException {
        try {

            // Create 2D List objects
            commands = new ArrayList<>();
            file_list = new ArrayList<>();
            pipes = new ArrayList<>();

            // Reset the number of commands
            num_commands = 0;

            // Reset the Path
            wkdir = "";

            // Open the xml file
            FileInputStream fis = new FileInputStream(file);

            // Create Document containing XML elements
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(fis);

            // Get root node of Document and put children in the node list
            Element pnode = doc.getDocumentElement();
            NodeList nodes = pnode.getChildNodes();

            // Iterate through each node
            for (int idx = 0; idx < nodes.getLength(); idx++) {
                Node node = nodes.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    Element elem = (Element) node;
                    parseCommand(elem);
                }
            }
        } catch (ParserConfigurationException | SAXException | IOException e) {
            System.out.println(e.getMessage());
        }
    }

    public int getNumCommands() {
        return commands.size();
    }

    public int getNumPipes() {
        return pipes.size();
    }

    public String getWkdir() {
        return wkdir;
    }

    private Command parseCommand(List<String> cmd_list) throws ProcessException {
        Command command = new Command();

        // Set the command's id
        command.setId(cmd_list.get(0));

        // Set the command's path
        command.setPath(cmd_list.get(1));

        // Set the commands's args
        command.setCommands(cmd_list.get(2));

        // Find the command's inFile
        if (!cmd_list.get(3).equals("Null")) {
            int flag = 0;
            for (int i = 0; i < file_list.size(); i++) {
                if (cmd_list.get(3).equals(file_list.get(i).get(0))) {
                    command.setInFile(file_list.get(i).get(1));
                    flag = 1;
                }
            }
            if (flag == 0) {
                System.out.println("File not found");
                throw new ProcessException("File not found");
            }
        }


        // Find the command's outFile
        if (!cmd_list.get(4).equals("Null")) {
            int flag = 0;
            for (int i = 0; i < file_list.size(); i++) {
                if (cmd_list.get(4).equals(file_list.get(i).get(0))) {
                    command.setOutFile(file_list.get(i).get(1));
                    flag = 1;
                }
            }
            if (flag == 0) {
                System.out.println("File not found");
                throw new ProcessException("File not found");
            }
        }

        return command;
    }

    // Return the next command in the command list
    public Command getCommand() throws ProcessException {

        if (commands.isEmpty()) {
            return null;
        }

        Command command;

        command = parseCommand(commands.get(0));

        // Remove this command
        commands.remove(0);
        return command;
    }

    // Return the next pipe in the pipe list
    public PipeCommand getPipe() throws ProcessException {
        PipeCommand pipe = new PipeCommand();

        // Get the next pipe
        List<String> pipe_temp = pipes.get(0);
        pipe.setId(pipe_temp.remove(0));

        // Build each command
        for (int i = 0; i < pipe_temp.size(); i += 5) {
            Command command = parseCommand(pipe_temp.subList(i, (i + 5)));
            pipe.addCommand(command);
        }

        pipes.remove(0);
        return pipe;
    }

    // Check the element for the given attribute
    private String parseElement(Element elem, String attribute)
            throws ProcessException {

        String temp;
        temp = elem.getAttribute(attribute);
        if ("".equals(temp)) {
            if (attribute.equals("id")) {
                System.out.println("No command id");
                throw new ProcessException("No command id");
            }
            return "Null";
        } else {
            return temp;
        }
    }

    // Parse the attributes in the element
    private void parseCommand(Element elem) throws ProcessException {

        List<String> temp = new ArrayList<>();

        // Check the element name
        String cmdName = elem.getNodeName();

        if (cmdName == null) {
            System.out.println("Error processing element");
            throw new ProcessException("Error processing element");

        } else if ("wd".equalsIgnoreCase(cmdName)) {
            System.out.println("Parsing wd");
            wkdir = parseElement(elem, "path");

        } else if ("file".equalsIgnoreCase(cmdName)) {
            System.out.println("Parsing file");

            temp.add(parseElement(elem, "id"));
            temp.add(parseElement(elem, "path"));

            file_list.add(temp);

        } else if ("cmd".equalsIgnoreCase(cmdName)) {
            System.out.println("Parsing cmd");

            temp.add(parseElement(elem, "id"));
            temp.add(parseElement(elem, "path"));
            temp.add(parseElement(elem, "args"));
            temp.add(parseElement(elem, "in"));
            temp.add(parseElement(elem, "out"));

            commands.add(temp);

        } else if ("pipe".equalsIgnoreCase(cmdName)) {
            System.out.println("Parsing pipe");
            temp.add(parseElement(elem, "id"));

            NodeList nodes = elem.getChildNodes();

            // Iterate through each node
            for (int idx = 0; idx < nodes.getLength(); idx++) {
                Node node = nodes.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    Element pipe_elem = (Element) node;

                    temp.add(parseElement(pipe_elem, "id"));
                    temp.add(parseElement(pipe_elem, "path"));
                    temp.add(parseElement(pipe_elem, "args"));
                    temp.add(parseElement(pipe_elem, "in"));
                    temp.add(parseElement(pipe_elem, "out"));
                }
            }

            pipes.add(temp);

        } else {
            System.out.println("Unknown Command");
            throw new ProcessException("Unknown Command");
        }
    }
}
